package org.example;

import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.WString;
import com.sun.jna.ptr.IntByReference;
import org.example.library.WintunLibrary;
import org.pcap4j.packet.*;
import org.pcap4j.packet.namednumber.IpNumber;
import org.pcap4j.packet.namednumber.IpVersion;
import org.pcap4j.packet.namednumber.UdpPort;

import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

public class TestMain {
    public static void main(String[] args) throws Exception {

        String name = "Test";
        String tunnelType = "Wintun";
        //创建网络适配器
        Pointer adapterHandle = WintunLibrary.INSTANCE.WintunCreateAdapter(new WString(name), new WString(tunnelType), null);

        if (adapterHandle == null) {
            throw new RuntimeException("网络适配器创建失败！(可能没用管理员启动)");
        }

        setIP(name, "172.29.1.1", "255.255.255.0");


        //启动适配器
        Pointer sessionHandle = WintunLibrary.INSTANCE.WintunStartSession(adapterHandle, 0x400000);

        var t = System.currentTimeMillis();
        while (true) {

            //读取适配器数据包
            IntByReference incomingPacketSize = new IntByReference();
            Pointer incomingPacket = WintunLibrary.INSTANCE.WintunReceivePacket(sessionHandle, incomingPacketSize);

            if (incomingPacket != null) {
                try {
                    int packetSize = incomingPacketSize.getValue();
                    byte[] packetBytes = incomingPacket.getByteArray(0, packetSize);

                    //解析数据包
                    IpPacket packet = (IpPacket) IpSelector.newPacket(packetBytes, 0, packetBytes.length);
                    System.out.println(packet);

                    if (packet.getPayload() instanceof UdpPacket) {
                        var udpPacket = (UdpPacket) packet.getPayload();
                        var dstPort = udpPacket.getHeader().getDstPort().valueAsInt();
                        //认为查找房间
                        if (dstPort == 6112) {

                            String text = "我的自定义房间AA";
                            byte[] customText = text.getBytes(StandardCharsets.UTF_8);

                            // 1. 创建30字节数组，默认填充0x20（空格）
                            byte[] paddedBytes = new byte[31];
                            Arrays.fill(paddedBytes, (byte) 0x20); // 全部初始化为0x20

                            // 2. 拷贝原数据（确保不越界）
                            int copyLength = Math.min(customText.length, 31);
                            System.arraycopy(customText, 0, paddedBytes, 0, copyLength);

                            // 3. 转换为十六进制字符串
                            StringBuilder hexBuilder = new StringBuilder();
                            for (byte b : paddedBytes) {
                                hexBuilder.append(String.format("%02x ", b));
                            }

                            var data = hexStringToByteArray("f7 30 93 00 50 58\n" +
                                    "33 57 1b 00 00 00 01 00 00 00 6e 8d b4 00 " + hexBuilder.toString() + " 00 00 01\n" +
                                    "03 49 07 01 01 a1 01 b9 49 01 13 8f e7 6f 4d 8b\n" +
                                    "61 71 73 5d 29 33 29 ad 43 6f 6f 75 79 43 61 bb\n" +
                                    "79 2f 77 33 6d 01 61 15 63 63 65 a3 8f 01 01 15\n" +
                                    "77 e3 3b 25 fd 81 5d 73 43 77 39 53 93 41 d1 39\n" +
                                    "73 ad cb 3f 77 21 00 02 00 00 00 09 00 00 00 01\n" +
                                    "00 00 00 02 00 00 00 20 00 00 00 e0 17         \n");

                            //房间1
                            var packetData1 = buildUdpPacket(packet, InetAddress.getByName("172.29.1.10"), data);
                            writeData(sessionHandle, packetData1);
                            //房间2
                            var packetData2 = buildUdpPacket(packet, InetAddress.getByName("172.29.1.11"), data);
                            writeData(sessionHandle, packetData2);
                            //房间3
                            var packetData3 = buildUdpPacket(packet, InetAddress.getByName("172.29.1.12"), data);
                            writeData(sessionHandle, packetData3);


                        }
                    }

                } catch (Exception e) {
                    throw new RuntimeException(e);
                } finally {
                    WintunLibrary.INSTANCE.WintunReleaseReceivePacket(sessionHandle, incomingPacket);
                }


            } else {

                int lastError = Native.getLastError();
                if (lastError == 0x103) {
                    //没数据等待数据
                    Pointer readWaitEvent = WintunLibrary.INSTANCE.WintunGetReadWaitEvent(sessionHandle);
                } else {
                    WintunLibrary.INSTANCE.WintunCloseAdapter(sessionHandle);
                    throw new RuntimeException("数据包读取失败，错误码：" + lastError);
                }
            }

        }
    }


    private static byte[] buildUdpPacket(IpPacket ipPacket, InetAddress dstAddrNew, byte[] data) throws UnknownHostException {
        InetAddress srcAddr = ipPacket.getHeader().getSrcAddr();
        InetAddress dstAddr = dstAddrNew;

        UdpPacket udpPacket = (UdpPacket) ipPacket.getPayload();
        UnknownPacket.Builder unknownBuilder = new UnknownPacket.Builder();
        unknownBuilder.rawData(data);
        //创建一个udp包
        UdpPacket.Builder udpBuilder = new UdpPacket.Builder()
                .srcPort(udpPacket.getHeader().getDstPort())
                .dstPort(udpPacket.getHeader().getSrcPort())
                .payloadBuilder(unknownBuilder)
                .correctLengthAtBuild(true);


        //创建一个ip包
        IpPacket newIpPacket = new IpV4Packet.Builder()
                .version(IpVersion.IPV4)
                .ttl((byte) 45)
                .tos(IpV4Rfc1349Tos.newInstance((byte) 0))
                .protocol(IpNumber.UDP)
                .srcAddr((Inet4Address) dstAddr)
                .dstAddr((Inet4Address) srcAddr)
                .correctLengthAtBuild(true)
                .correctChecksumAtBuild(true)
                .payloadBuilder(udpBuilder)
                .identification((byte) 1000)
                .build();

        return newIpPacket.getRawData();
    }

    private static void writeData(Pointer pointer, byte[] data) {

        Pointer packetPointer = WintunLibrary.INSTANCE.WintunAllocateSendPacket(pointer, data.length);

        if (packetPointer != null) {

            packetPointer.write(0, data, 0, data.length);

            // 发送数据包
            WintunLibrary.INSTANCE.WintunSendPacket(pointer, packetPointer);

        } else {
            System.err.println("Failed to allocate send packet.");
        }
    }

    public static void setIP(String interfaceName, String ipAddress, String subnetMask) throws IOException {
        String command = String.format("netsh interface ip set address name=\"%s\" static %s %s",
                interfaceName, ipAddress, subnetMask);
        Process process = Runtime.getRuntime().exec(command);
        try {
            process.waitFor();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static byte[] hexStringToByteArray(String s) {
        s = s.replaceAll("\\s+", "");
        int len = s.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            // 将每两个字符转换成一个字节
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                    + Character.digit(s.charAt(i + 1), 16));
        }
        return data;
    }

}